﻿#include <QSemaphore>
#include <QThread>
#include <QDebug>

#include "LoopBuffer.h"

const bool debug = false;

static int total = 0;

int LoopBuffer::getAudioSuggestSize(int sample,int channel,int sampleBits)
{
    return (sample * channel * sampleBits/2) * 1 + 1024;
}

LoopBuffer::LoopBuffer(qint64 size, bool multiThread):m_sizeMax(size),m_wpos(0),m_rpos(0),m_multiThread(multiThread),m_mode(WriteTotalLimited)
{
    m_buffer = new QByteArray(m_sizeMax,0);
    Q_ASSERT(m_buffer);

    m_mode = WriteTotalUnlimited;

    m_remain = m_sizeMax;

    if (m_multiThread) {
        m_sem = new QSemaphore;
    }
}

LoopBuffer::~LoopBuffer()
{
    delete m_buffer;
    if (m_multiThread && m_sem) {
        delete m_sem;
    }
}

qint64 LoopBuffer::size() const
{
    return m_sizeMax;
}

qint64 LoopBuffer::usedSize() const
{
    return m_sizeMax - m_remain;
}

qint64 LoopBuffer::unusedSize() const
{
    return m_remain;
}

void LoopBuffer::clear()
{
    m_wpos = 0;
    m_rpos = 0;
    m_remain = m_sizeMax;
    if (m_multiThread && m_sem) {
        delete m_sem;
        m_sem = nullptr;
        m_sem = new QSemaphore;
    }
}

bool LoopBuffer::open(OpenMode mode)
{
    return QIODevice::open(mode | QIODevice::Unbuffered);
}

void LoopBuffer::setWriteMode(WriteMode mode)
{
    m_mode = mode;
}

void LoopBuffer::setAudioParam(int sampleSize,int channel)
{
    m_sampleBits = sampleSize;
    m_channel = channel;
}

void LoopBuffer::boundaryTest()
{
    if (m_remain < 0)
        Q_ASSERT(false);

    if (m_remain == 0 && m_rpos != m_wpos)
        Q_ASSERT(false);

    if (m_remain != 0) {
        if (m_wpos > m_rpos) {
            if (m_sizeMax - m_wpos + m_rpos != m_remain) {
                Q_ASSERT(false);
            }
        }

        if (m_rpos > m_wpos) {
            if (m_sizeMax - m_rpos + m_wpos != (m_sizeMax - m_remain))
                Q_ASSERT(false);
        }
    }
}

void LoopBuffer::dumpInfo(const QString &info, QVariant value)
{
    if (debug) {
        qDebug() << "vvvv dumpInfo begin vvvv";
        qDebug() << info << value;
        qDebug() << "m_rpos" << m_rpos;
        qDebug() << "m_wpos" << m_wpos;
        qDebug() << "remain" << m_remain;
        qDebug() << "max size" << m_sizeMax;
        qDebug() << "^^^^ dumpInfo end   ^^^^";
    }
}

QByteArray LoopBuffer::readData(qint64 maxlen)
{
    QByteArray result(maxlen,0);
    read(result.data(),maxlen);
    return result;
}

qint64 LoopBuffer::readData(char *data, qint64 maxlen)
{
    /* 1.读取超过总容量。2.缓冲为空。3.可读数据不够*/
    Q_ASSERT(maxlen);

    if (debug)
        qDebug() << "READ" << maxlen;
    if (m_multiThread) {
        bool flag = false;
        while (!flag) {
            flag = m_sem->tryAcquire(maxlen,100);
            m_readNullSize++;
            if (m_readNullSize >= 5) {
                if (debug)
                    qDebug() << "buffer end";
                return 0;
            }
        }
    }

    if (maxlen > m_sizeMax || (m_remain == m_sizeMax) || (m_sizeMax - m_remain < maxlen)){
        /* 作为通用的环形缓冲，返回值应为0，但是对于Qt的音频输出，那么需要返回值，否则认为是没有数据 */
        if (debug)
            qDebug() << "read not ok" << maxlen;
        QVector<char> vec(maxlen,0);
        memcpy(data,vec.data(),maxlen);
        return maxlen;
    }

    if(m_rpos + maxlen <= m_sizeMax) {
        memcpy(data,m_buffer->data() + m_rpos,maxlen);
        m_rpos += maxlen;
        m_remain += maxlen;
    } else {
        int remain = m_sizeMax - m_rpos;
        memcpy(data,m_buffer->data() + m_rpos,remain);
        memcpy(data + remain, m_buffer->data(),maxlen - remain);
        m_rpos = maxlen - remain;
        m_remain += maxlen;
    }

    dumpInfo("read",maxlen);
    boundaryTest();
    m_readNullSize = 0;

    return maxlen;
}

qint64 LoopBuffer::writeData(const char *data, qint64 len)
{
    Q_ASSERT(len);
    if(debug)
        qDebug() << "WRITE" << QThread::currentThreadId() << len << m_sizeMax;

    switch (m_mode) {
    case WriteTotalLimited:
        if (len > m_sizeMax ) {
            return 0;
        }
        break;
    case WriteTotalUnlimited:
        if (len > m_sizeMax) {
            // 当数据较多，则buffer值只留存最后一个sizeMax单位的数据
            // 调整rpos和remain，可以认为，这仅仅是个普通一个单位的复制，
            // 在调整地址前，就是一个整体复制，那么复制前的，也应该是充满的
            data += (len - m_sizeMax);
            m_wpos = (m_wpos + len % m_sizeMax) % m_sizeMax;
            m_rpos = m_wpos;
            m_remain = 0;
            len = m_sizeMax;
        }
        break;
    case WriteRemainLimited:
        if (len > m_remain) {
            if (debug)
                qWarning("write to long %d",len);
            return 0;
        }
        break;
    default:
        qDebug() << "error";
        break;
    }

    if (m_wpos + len <= m_sizeMax) {
        memcpy(m_buffer->data() + m_wpos,data,len);
        m_wpos =  (m_wpos + len) % m_sizeMax;
        m_remain = (m_remain - len < 0) ? 0 : m_remain -len;
        if (m_remain <= 0)
            m_rpos = m_wpos;

    } else {
        int remain = m_sizeMax - m_wpos;
        memcpy(m_buffer->data() + m_wpos,data,remain);
        memcpy(m_buffer->data(),data + remain,len - remain);
        m_wpos = len - remain;
        m_remain = (m_remain - len < 0) ? 0 : m_remain -len;
        if (m_wpos > m_rpos || m_remain == 0) {
            // reset read pos
            m_rpos = m_wpos;
            m_remain = 0;
        }
    }

//    qDebug() << "used" << m_sizeMax - m_remain;

    dumpInfo("write",len);
    boundaryTest();

    if (m_multiThread) {
        m_sem->release(len);
    }
    return len;
}


